package org.tech4j.captcha.character.provider;

import com.google.common.base.Throwables;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.tech4j.captcha.api.CaptchaStore;
import org.tech4j.captcha.api.CaptchaStoreFactory;
import org.tech4j.captcha.api.model.CaptchaEntity;
import org.tech4j.captcha.api.model.CaptchaResponse;
import org.tech4j.captcha.api.model.VoidFunction;
import org.tech4j.captcha.character.exception.ResponseCode;
import org.tech4j.captcha.character.model.CharacterCaptchaEntity;
import org.tech4j.common.spi.Join;

import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @program: tech4j
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/4 14:49
 **/
@Join
public class Tech4JCaptchaProvider implements CaptchaProvider {

    @Override
    public CaptchaResponse send(CharacterCaptchaEntity captchaEntity) {
        return execute(captchaEntity);
    }

    @Override
    public <R> CaptchaResponse send(CharacterCaptchaEntity captchaEntity, Function<CaptchaEntity, R> function) {
        CaptchaResponse result = execute(captchaEntity);
        if (result.getSuccess() && function != null) {
            try {
                R data = function.apply(captchaEntity);
                return CaptchaResponse.ofSuccess(data);
            } catch (Exception e) {
                return CaptchaResponse.ofFailed(
                        ResponseCode.CAPTCHA_CUSTOM_FUNCTION_ERROR.getErrorCode(),
                        String.format(ResponseCode.CAPTCHA_CUSTOM_FUNCTION_ERROR.getMessage(), Throwables.getStackTraceAsString(e)));
            }
        }
        return result;
    }

    private CaptchaResponse execute(CharacterCaptchaEntity captchaEntity) {
        Supplier<String> supplier = captchaEntity.getCaptcha();
        String captcha = supplier == null ? generateCaptchaCode() : supplier.get();
        if (StringUtils.isBlank(captcha)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_CODE_EMPTY.getErrorCode(), ResponseCode.CAPTCHA_CODE_EMPTY.getMessage());
        }
        Function<CaptchaEntity, CaptchaResponse> sendFunction = captchaEntity.getSendFunction();
        if (sendFunction == null) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_SEND_FUNCTION_ERROR.getErrorCode(),
                    String.format(ResponseCode.CAPTCHA_CODE_EMPTY.getMessage(), "sendFunction is null"));
        }
        // 发送验证码
        CaptchaResponse result = sendFunction.apply(captchaEntity);
        if (result.isFailed()) {
            return result;
        }
        // 保存验证码
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        if (Objects.isNull(captchaStore)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_STORE_EMPTY.getErrorCode(), ResponseCode.CAPTCHA_STORE_EMPTY.getMessage());
        }
        try {
            captchaStore.storeCaptcha(captchaEntity.getKey(), captcha, captchaEntity.getExpireAt(), captchaEntity.getTimeUnit());
        } catch (Exception e) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_STORE_ERROR.getErrorCode(), Throwables.getStackTraceAsString(e));
        }
        return CaptchaResponse.ofSuccess();
    }

    @Override
    public CaptchaResponse validate(CharacterCaptchaEntity captchaEntity, Supplier<String> supplier, VoidFunction function) {
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        if (Objects.isNull(captchaStore)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_STORE_EMPTY.getErrorCode(), ResponseCode.CAPTCHA_STORE_EMPTY.getMessage());
        }
        String captcha = captchaStore.readCaptcha(captchaEntity.getKey());
        if (StringUtils.isBlank(captcha)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_EXPIRED.getErrorCode(), ResponseCode.CAPTCHA_EXPIRED.getMessage());
        }
        if (!StringUtils.equals(captcha, supplier.get())) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_ERROR.getErrorCode(), ResponseCode.CAPTCHA_ERROR.getMessage());
        }
        captchaStore.removeCaptcha(captchaEntity.getKey());

        if (function != null) {
            function.apply();
        }
        return CaptchaResponse.ofSuccess();
    }

    @Override
    public CaptchaResponse validate(CharacterCaptchaEntity captchaEntity, Predicate<String> predicate, VoidFunction function) {
        CaptchaStore captchaStore = CaptchaStoreFactory.newInstance(captchaEntity.getStoreType());
        if (Objects.isNull(captchaStore)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_STORE_EMPTY.getErrorCode(), ResponseCode.CAPTCHA_STORE_EMPTY.getMessage());
        }
        String captcha = captchaStore.readCaptcha(captchaEntity.getKey());
        if (StringUtils.isBlank(captcha)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_EXPIRED.getErrorCode(), ResponseCode.CAPTCHA_EXPIRED.getMessage());
        }
        if (!predicate.test(captcha)) {
            return CaptchaResponse.ofFailed(ResponseCode.CAPTCHA_ERROR.getErrorCode(), ResponseCode.CAPTCHA_ERROR.getMessage());
        }
        captchaStore.removeCaptcha(captchaEntity.getKey());
        if (function != null) {
            function.apply();
        }
        return CaptchaResponse.ofSuccess();
    }

    private final static int randomStartInt = 100000;
    private final static int randomEndInt = 999999;
    protected String generateCaptchaCode() {
        return String.valueOf(RandomUtils.insecure().randomInt(randomStartInt, randomEndInt));
    }
}
